/*----------------------------------------------------------------------------*/
/* Nombre archivo:      CorreoAdmin.java                                      */
/* Descripción:         Realiza el evío de correos.                           */
/* Autor:               Adalberto Montes Hernández                            */
/* Fecha:               Junio 16/2011                                         */
/* Última modificación: Junio 16/2011                                         */
/* Version:             1.0                                                   */
/*----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
 Historico
 Junio 16/2011 archivo creado.
 -----------------------------------------------------------------------------*/

package co.edu.udea.herbario.util;

import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.ResourceBundle;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.log4j.Logger;


/**
 * Resaliza el envío de correos desde la aplicacion.
 * Debe existir el proyecto la libreria log4j, mail y activation
 * @author Adalberto Montes
 * @version 1.0, Junio 16/2011
 * @since 1.5(JDK), Junio 16/2011
 */
public class CorreoAdmin
{
    /**
     * Nombre de la propiedad para obtener el host del correo
     */
    private static String PROPIEDAD_HOST = "mail.host";
    /**
     * Nombre de la propiedad para obtener el parametro si necesita tls
     */
    private static String PROPIEDAD_TLS = "mail.tsl";
    /**
     * Nombre de la propiedad para obtener el puerto del correo
     */
    private static String PROPIEDAD_PUERTO = "mail.puerto";
    /**
     * Nombre de la propiedad para obtener el usuario del correo
     */
    private static String PROPIEDAD_USUARIO = "mail.usuario";
    /**
     * Nombre de la propiedad para obtener si el servidor necesita o no autenticación
     */
    private static String PROPIEDAD_AUTENTICACION = "mail.autenticacion";

    /**
     * Objeto para el manejo de las excepciones
     */
    private Logger logger = Logger.getLogger(getClass());
    /**
     * Host origen desde donde se envía el correo electrónico
     */
    private String host = null;
    /**
     * Si utililiza TLS (Transport Layer Security) es la evolucion del SSL: true o false
     */
    String tls = null;
    /**
     * Puerto para envio de correos
     */
    String puerto = null;
    /**
     * Nombre del usuario para autenticacion en el servidor
     */
    String usuario = null;
    /**
     * Clave para autenticacion en el servidor
     */
    String clave = null;
    /**
     * Si requiere o no usuario y password para conectarse y enviar el correo: true o false
     */
    String autenticacion = null;
    /**
     * Objeto para la configuración de las propiedades de la conexión
     */
    private Properties props;
    /**
     * Objeto para la configuración de las sesion del envío del correo, representa la conexion con el servidor de correo
     */
    private Session session;

    /**
     * Valor de la excepcion en caso de que se genere
     */
     private Exception exception;

     /**
      * Instancia de la clase
      */
     private static CorreoAdmin instance = null;

    /**
     * Captura una nueva instancia del administrador de correo
     * @return instancia de CorreoAdmin
     */
    public static synchronized CorreoAdmin getInstance() {
        if (instance == null) {
            instance = new CorreoAdmin();
        }
        return instance;
    }

    /**
     * Constructor de la clase para cargar las propiedades del envío del correo
     * Debe existir el archivo de propiedades orgSistemas.properties y
     * la propiedad host desde donde se envía el correo, el envio se puede realizar de manera autenticada o no
     */
    public CorreoAdmin() {
        ResourceBundle bundle = ResourceBundle.getBundle(ConstantesStatic.PROPERTIES_ORGSISTEMAS);
        host = bundle.getString(PROPIEDAD_HOST);
        autenticacion = bundle.getString(PROPIEDAD_AUTENTICACION);
        puerto = bundle.getString(PROPIEDAD_PUERTO);
        tls = bundle.getString(PROPIEDAD_TLS);
        usuario = bundle.getString(PROPIEDAD_USUARIO);

        props = System.getProperties();

        // Nombre del host de correo
        if(!empty(host))props.put("mail.smtp.host", host);

        // TLS si está disponible
        if(!empty(tls))props.setProperty("mail.smtp.starttls.enable", tls);

        // Puerto para envio de correos
        if(!empty(puerto))props.setProperty("mail.smtp.port", puerto);

        // Nombre del usuario
        if(!empty(usuario))props.setProperty("mail.smtp.user", usuario);

        // Si requiere o no usuario y password para conectarse.
        if(!empty(autenticacion))props.setProperty("mail.smtp.auth", autenticacion);

        session = Session.getDefaultInstance(props, null);
    }

    /**
     * Método que realiza el envío del correo
     * @param destino Direcció destino del correo
     * @param asunto Asunto del correo
     * @param cuerpoHTML Cuerpo del correo, preferiblemente en HTML
     * @param remitente Dirección del remitente
     * @param adjuntos Collection con los archivos adjuntos del correo
     * @return true si realiza correctamente el envío, false en caso contrario
     */
    public synchronized boolean enviar(String destino, String asunto, String cuerpoHTML, String remitente, List<File> adjuntos){
        boolean blnRetorno=true;
        try{
            // Definición del mensaje
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(remitente));
            message.setRecipients(Message.RecipientType.TO, destino);
            message.setSubject(asunto);
            BodyPart texto = new MimeBodyPart();
            texto.setText(cuerpoHTML);
            //recorre y convierte los archivos al objeto que soporta el correo
            List<BodyPart> bp = new LinkedList<BodyPart>();
            if(adjuntos != null){
            	for(int i = 0; i < adjuntos.size(); i++){
                    BodyPart adjunto = new MimeBodyPart();
                    adjunto.setDataHandler(new DataHandler(new FileDataSource(adjuntos.get(i))));
                    adjunto.setFileName(adjuntos.get(i).getName());
                    bp.add(adjunto);
            	}
            }
            //Una MultiParte para agrupar texto e imagen.
            MimeMultipart multiParte = new MimeMultipart();
            multiParte.addBodyPart(texto);
            //adjunta los archivos al mensaje
            Iterator<BodyPart> it = bp.iterator();
            while(it.hasNext()){
            	BodyPart attach =(BodyPart)it.next();
            	multiParte.addBodyPart(attach);//<-----------------finalmente lo añadimos al mensaje
            }
            //message.setContent(cuerpoHTML,"text/html");
            message.setContent(multiParte);
            //Transport = Objeto para el envío de mensajes
            Transport.send(message, message.getAllRecipients());

        }catch(Exception e) {
            logger.error(e, e);
            this.exception = e;
            blnRetorno=false;
        }
        return blnRetorno;
    }


    /**
     * Método que realiza el envío del correo con autenticacion al servidor
     * @param destino Direcció destino del correo
     * @param asunto Asunto del correo
     * @param cuerpoHTML Cuerpo del correo, preferiblemente en HTML
     * @param remitente Dirección del remitente
     * @param adjuntos Collection con los archivos adjuntos del correo
     * @return true si realiza correctamente el envío, false en caso contrario
     */
    public synchronized boolean enviarAutenticado(String destino, String asunto, String cuerpoHTML, String remitente, List<File> adjuntos){
        boolean blnRetorno=true;
        try{
            // Definición del mensaje
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(remitente));
            message.setRecipients(Message.RecipientType.TO, destino);
            message.setSubject(asunto);
            BodyPart texto = new MimeBodyPart();
            texto.setText(cuerpoHTML);
            //recorre y convierte los archivos al objeto que soporta el correo
            List<BodyPart> bp = new LinkedList<BodyPart>();
            if(adjuntos != null){
            	for(int i = 0; i < adjuntos.size(); i++){
                    BodyPart adjunto = new MimeBodyPart();
                    adjunto.setDataHandler(new DataHandler(new FileDataSource(adjuntos.get(i))));
                    adjunto.setFileName(adjuntos.get(i).getName());
                    bp.add(adjunto);
            	}
            }
            //Una MultiParte para agrupar texto e imagen.
            MimeMultipart multiParte = new MimeMultipart();
            multiParte.addBodyPart(texto);
            //adjunta los archivos al mensaje
            Iterator<BodyPart> it = bp.iterator();
            while(it.hasNext()){
            	BodyPart attach =(BodyPart)it.next();
            	multiParte.addBodyPart(attach);//<-----------------finalmente lo añadimos al mensaje
            }
            //message.setContent(cuerpoHTML,"text/html");
            message.setContent(multiParte);

            Transport t = session.getTransport("smtp");
            //Ahora debemos establecer la conexión, dando el nombre de usuario y password.
            t.connect(host, usuario, clave);
            t.sendMessage(message,message.getAllRecipients());
            t.close();

        }catch(Exception e) {
            logger.error(e, e);
            setException(e);
            blnRetorno=false;
        }
        return blnRetorno;
    }

    /**
     * Devuelve la excepcion que se genere en el envío del correo
     * @return the exception
     */
    public synchronized Exception getException() {
        return exception;
    }

    /**
     * Asigna la excepcion que se genere en el envío del correo
     */
    public synchronized void setException(Exception exception) {
        this.exception = exception;
    }

    /**
     * Pregunta si un objeto es vacio o nulo
     * @param objeto Objeto
     * @return true o false
     */
    private boolean empty(Object objeto) {
            if (objeto == null) return true;
            if (objeto instanceof String) return objeto.equals("");
            return false;
    }

}
